package jplat.core.trans;

import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.JsonElement;

import ebcs.scan.bean.sys.SysUserInfor;
import jplat.base.app.parser.IAppPackParser;
import jplat.base.constant.KPlatResponseCode;
import jplat.core.session.JSession;
import jplat.core.session.JSessionFactory;
import jplat.core.session.redis.JRedisSession;
import jplat.error.exception.JSystemException;
import jplat.error.exception.JTransException;
import jplat.tools.coder.JsonCoder;
import jplat.tools.data.validate.JBeanValidateUtils;

/**
 * 一次服务请求的上下文.
 * 比如App发起一次请求.
 * 但是一次请求可以对应多个ICOP的请求JTransContext
 * 此类应该为接口,以后再优化吧.
 * @author zhangcq
 * @date Nov 22, 2016
 * @comment
 */
public class JAppContext
{
	//实现类才会有这两个对象
	//其实对应从层来说，它连用的什么协议都不应该知道.
	private HttpServletRequest request;
	private HttpServletResponse response;
	
	//相应信息和错误码.
	private String retCode;
	private String retMsg;
	
	//是否创建新建的会话.
	boolean isNewSession;
	
	//请求头. 如果想要平台利用head内容做些事情，
	//那么就需要定义一个准确的类，不然如果在其他地方改变了这个类
	//那么平台就会出问题.
	//可以继承，然后自定义.
	private JClientReqHeader reqHeader;
	
	//交易请求体
	private int packState = PACK_STATE_INIT;   
	private Object reqBody;
	
	//请求响应体
	private Object rspBody;
	
	//客户信息
	private SysUserInfor userInfo;

	
	//请求基本信息
	private JAppConnectInfo reqInfo;
	
	//App请求解析器
	private IAppPackParser appParser;
	
	//会话实现类.
	private int sessType;
	private JSession session;
	
	//报文状态
	public static final int PACK_STATE_INIT = 0;		//初始状态.
	public static final int PACK_STATE_JSONEL = 1;		//JSONElemet
	public static final int PACK_STATE_CLAZZ = 2;		//Class
	
	public JAppContext()
	{
		retCode = KPlatResponseCode.CD_SUCCESS;
		retMsg = KPlatResponseCode.MSG_SUCCESS;
	}
	
	/**
	 * 创建会话,会话是会话,会话只是一个保存状态的地方.
	 * 会话的数据代表是会话令牌(即一个字符串,即ttnk2中的字符.),
	 * 会话ID只是状态的标识，可以放到HTTP-header头中.
	 * 
	 * 身份认证是另外的认证令牌，可以放到请求头中.
	 * 当需要客户信息的时候发到请求头中.
	 * 为了以后缓存迁移，先把会话令牌返回给客户端，让客户端先有
	 * 处理令牌的能力.
	 * @author zhangcq
	 * @date Dec 22, 2016
	 * @comment 
	 * @param create 是否强制建一个会话. true--创建,即使已经有会话了也会重新创建一个新会话.
	 * @return
	 * @throws JSystemException 
	 */
	public JSession createSession( boolean create ) throws JSystemException
	{
		if ( session != null )
		{
			return session;
		}
		//this.setSessType(JSession.SESS_REDIS);
		session = JSessionFactory.createJSession(this, sessType, create );
		isNewSession = session.isNew();
		
		return session;
	}
	
	public boolean clearSession()  throws JSystemException{
		return JSessionFactory.cleanSession(this);
	}

	/**
	 * 不会强制创建,已有则返回，无则抛出会话超时的异常.
	 * @author zhangcq
	 * @date Dec 22, 2016
	 * @comment 
	 * @return
	 * @throws JSystemException
	 */
	public JSession getSession() throws JSystemException
	{
		return createSession(false);
	}

	/**
	 * 不带客户追踪标记,常用.
	 * @author zhangcq
	 * @date Dec 14, 2016
	 * @comment 
	 * @return
	 */
	public JTransContext createTransContext()
	{
		JTransContext context = new JTransContext();
		
		String userInfoMark = "";
		if( userInfo != null )
		{
			userInfoMark = userInfo.getUserNo();
		}
		
		context.setUserInfoMark(userInfoMark);
		return context;
	}
	
	/**
	 * 追踪标记，比如注册时的客户手机号，身份证号等.
	 * @author zhangcq
	 * @date Dec 14, 2016
	 * @comment 
	 * @param traceMark
	 * @return
	 */
	public JTransContext createTransContext( String traceMark )
	{
		JTransContext context = createTransContext();
		
		context.setTransMark(traceMark);
		return context;
	}
	
	/**
	 * 保存返回数据和数据类型.
	 * @author zhangcq
	 * @date Dec 13, 2016
	 * @comment 
	 * @param rb
	 * @param type
	 */
	public void setReqBodyByType( Object rb, int type )
	{
		reqBody = rb;
		packState = type;
	}
	
	/**
	 * 获取请求对象.
	 * @author zhangcq
	 * @date Dec 8, 2016
	 * @comment 
	 * @param clazz
	 * @return
	 * @throws JSystemException 
	 */
	public <T> T obtainReqModel( Class<T> clazz ) throws JSystemException
	{
		return obtainReqModel( clazz, false );
	}
	
	/**
	 * 获取请求对象，并校验数据是否为空.
	 * @author zhangcq
	 * @date Jan 20, 2017
	 * @comment 
	 * @param clazz
	 * @param validate
	 * @return
	 * @throws JSystemException
	 */
	public <T> T obtainReqModel( Class<T> clazz, boolean validate ) throws JSystemException
	{
		if (  PACK_STATE_CLAZZ == packState )
		{
			return (T)reqBody;
		}
		
		//无法获取请求数据.
		if ( reqBody == null )
		{
			throw new JSystemException(KPlatResponseCode.CD_INPUT_UNAVAIABLE,KPlatResponseCode.MSG_INPUT_UNAVAIABLE);
		}
	
		reqBody = JsonCoder.fromJson((JsonElement)reqBody, clazz);
		packState = PACK_STATE_CLAZZ;
		
		//进行数据校验.
		if ( validate )
		{
			JBeanValidateUtils.validate(reqBody);
		}
		
		return (T)reqBody; 
	}
	
	/**
	 * 设置失败信息.
	 * @author zhangcq
	 * @date Dec 14, 2016
	 * @comment 
	 * @param errcode
	 * @param errmsg
	 * @return
	 */
	public JAppContext fail( String errcode, String errmsg )
	{
		retCode = errcode;
		retMsg = errmsg;
		
		return this;
	}
	
	/**
	 * 设置失败信息.
	 * @author zhangcq
	 * @date Dec 14, 2016
	 * @comment 
	 * @param errcode
	 * @param errmsg
	 * @return
	 */
	public JAppContext failTransException( String errcode, String errmsg ) throws JTransException
	{
//		retCode = errcode;
//		retMsg = errmsg;
		
		throw new JTransException(errcode,errmsg);
	}
	
	
	/**
	 * 设置失败信息.
	 * @author zhangcq
	 * @date Dec 14, 2016
	 * @comment 
	 * @param errcode
	 * @param errmsg
	 * @return
	 */
	public JAppContext failSystemException( String errcode, String errmsg ) throws JSystemException
	{
//		retCode = errcode;
//		retMsg = errmsg;
		
		throw new JSystemException(errcode,errmsg);
	}
	
	/**
	 * 配置请求参数.
	 * @author zhangcq
	 * @date Dec 14, 2016
	 * @comment 
	 * @param request
	 * @param response
	 */
	public void config( HttpServletRequest request, HttpServletResponse response )
	{
		this.request = request;
		this.response = response;
	}
	
	/**
	 * 返回数据.
	 * @author zhangcq
	 * @date Dec 14, 2016
	 * @comment 
	 * @param __rspBody
	 * @return
	 */
	public void buildAppResponse( Object __rspBody )
	{
		rspBody = __rspBody;
		if ( __rspBody == null )
		{
			rspBody = new HashMap<String,String>();
		}
		try {
			appParser.doAppPack(this);
		} catch (JSystemException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/***************/
	public String getRetCode() {
		return retCode;
	}

	public JAppContext setRetCode(String retCode) {
		this.retCode = retCode;
		
		return this;
	}

	public String getRetMsg() {
		return retMsg;
	}

	public JAppContext setRetMsg(String retMsg) {
		this.retMsg = retMsg;
		
		return this;
	}
	
	public Object getReqBody() {
		return reqBody;
	}

	public void setReqBody(Object reqBody) {
		this.reqBody = reqBody;
	}

	public Object getRspBody() {
		return rspBody;
	}

	public void setRspBody(Object rspBody) {
		this.rspBody = rspBody;
	}

	public JClientReqHeader getReqHeader() {
		return reqHeader;
	}

	public void setReqHeader(JClientReqHeader reqHeader) {
		this.reqHeader = reqHeader;
	}

	public HttpServletRequest getRequest() {
		return request;
	}

	public void setRequest(HttpServletRequest request) {
		this.request = request;
	}

	public HttpServletResponse getResponse() {
		return response;
	}

	public void setResponse(HttpServletResponse response) {
		this.response = response;
	}

	public JAppConnectInfo getReqInfo() {
		return reqInfo;
	}

	public void setReqInfo(JAppConnectInfo reqInfo) {
		this.reqInfo = reqInfo;
	}

	 

	public SysUserInfor getUserInfo() {
		return userInfo;
	}

	public void setUserInfo(SysUserInfor userInfo) {
		this.userInfo = userInfo;
	}

	public IAppPackParser getAppParser() {
		return appParser;
	}

	public void setAppParser(IAppPackParser appParser) {
		this.appParser = appParser;
	}

	public int getPackState() {
		return packState;
	}

	public void setPackState(int packState) {
		this.packState = packState;
	}
	
	public int getSessType() {
		return sessType;
	}

	public void setSessType(int sessType) {
		this.sessType = sessType;
	}

	public boolean isNewSession() {
		return isNewSession;
	}

	public void setNewSession(boolean isNewSession) {
		this.isNewSession = isNewSession;
	}
}
